#[derive(Debug)]
pub enum Operation {
    Add,
    Sub,
    Mul,
    Div,
}

/// An expression, in tree form.
#[derive(Debug)]
pub enum Expression {
    /// An operation on two subexpressions.
    Op {
        op: Operation,
        left: Box<Expression>,
        right: Box<Expression>,
    },

    /// A literal value
    Value(i64),
}

/// The result of evaluating an expression.
#[derive(Debug, PartialEq, Eq)]
pub enum Res {
    /// Evaluation was successful, with the given result.
    Ok(i64),
    /// Evaluation failed, with the given error message.
    Err(String),
}
// Allow `Ok` and `Err` as shorthands for `Res::Ok` and `Res::Err`.
use Res::{Err, Ok};

pub fn eval(e: Expression) -> Res {
    match e {
        Expression::Value(v) => Ok(v),
        Expression::Op { op, left, right } => {
            let left = match eval(*left) {
                Ok(v) => v,
                Err(e) => return Err(e),
            };
            let right = match eval(*right) {
                Ok(v) => v,
                Err(e) => return Err(e),
            };
            Ok(match op {
                Operation::Add => left + right,
                Operation::Sub => left - right,
                Operation::Mul => left * right,
                Operation::Div => {
                    if right == 0 {
                        return Err("Division by zero".to_string());
                    } else {
                        left / right
                    }
                }
            })
            // Operation::Add => Ok(eval(*left) + eval(*right)),
            // Operation::Sub => Ok(eval(*left) - eval(*right)),
            // Operation::Mul => Ok(eval(*left) * eval(*right)),
            // Operation::Div => match right {
            //     Ok(0) => Err(String::from("Division by zero")),
            //     Ok(r) => Ok(eval(*left).unwrap() / r),
            // },
        }
    }
}
